import tkinter as tk
from tkinter import messagebox
import random
from src.hardware.zlgcan.zlgcan import *
from src.hardware.uds.zuds import *
from src.hardware.uds_serve_lib import Serve_Func
from concurrent.futures import ThreadPoolExecutor


class Ecu_Unit_UI(tk.Toplevel):
    UDS_FUNC_DICT = {
        '10': '会话模式',
        '3e': '会话保持',
        '27': '安全访问',
        '22': '读取DID数据',
        '2e': '写入DID数据',
        '2f': 'IO控制',
        '31': '例程控制'
    }
    # '11': 'ECU复位',
    # '28': '通讯控制',

    _objDict = {}

    def __new__(cls, parent, rooter, unitConfig):
        '''
        同一个PosId只能有一个实例，避免窗口多开出现问题
        '''
        PosId = unitConfig.pos
        # ic_debugger(PosId)
        # ic_debugger(unitConfig.pos)
        # ic_debugger(cls._objDict)
        if PosId not in cls._objDict:
            newObj = object.__new__(cls)
            cls._objDict[PosId] = newObj
            return newObj
        else:
            # cls._objDict[PosId].resetTestState()
            # 将隐藏的窗口显示出来
            cls._objDict[PosId].deiconify()
            # return cls._objDict[PosId]

    def __init__(self, parent, rooter,  unitConfig) -> None:
        super().__init__()

        self.Parent = parent
        self.ROOTER = rooter
        self.UDS_SERVE_FUNC = rooter.UDS_SERVE_FUNC

        self.pool = ThreadPoolExecutor(
            max_workers=1, thread_name_prefix=f'Ecu_Unit_{unitConfig.pos}')

        self.unitConfig = unitConfig
        # ic_debugger(self.unitConfig)
        self.srcAddr = int(unitConfig.srcAddr, 16)
        self.dstAddr = int(unitConfig.dstAddr, 16)
        # ic_debugger(self.srcAddr, self.dstAddr)

        # self.TESTER = UI_CommonUnit_Tester(unitWin=self, masterUI=masterUI)

        self.title(self.unitConfig.name)

        self.viewInit()

        self.protocol('WM_DELETE_WINDOW', self.closeHandle)

    def closeHandle(self):
        messagebox.showinfo('隐藏', '点击关闭窗口按钮，隐藏此窗框.........')
        self.withdraw()

    def viewInit(self):

        # 初始化框架
        self.level1FrameInit()

    def level1FrameInit(self):
        self.titleFrame = tk.Frame(self)
        self.titleFrame.grid(row=1, column=1, padx=10, pady=10)
        self.manualStepFrame = tk.LabelFrame(self, text="常用功能列表")
        self.manualStepFrame.grid(row=2, column=1, padx=10, pady=10)
        self.titleFrame_Init()
        self.manualStepFrame_Init()

    def titleFrame_Init(self):
        self.title_lable = tk.Label(
            self.titleFrame, text=self.unitConfig.zh_name)
        self.title_lable.grid(row=1, column=1, padx=10,
                              pady=10, sticky=tk.NSEW)

        self.title_srcAddr = tk.Label(
            self.titleFrame, text=f'源地址：{self.unitConfig.srcAddr}')
        self.title_srcAddr.grid(row=1, column=2, padx=10,
                                pady=10, sticky=tk.NSEW)
        self.title_dstAddr = tk.Label(
            self.titleFrame, text=f'返回地址：{self.unitConfig.dstAddr}')
        self.title_dstAddr.grid(row=1, column=3, padx=10,
                                pady=10, sticky=tk.NSEW)

    def manualStepFrame_Init(self):
        self.funcFrames_dataInit()
        self.funcFrames_viewInit()

    def funcFrames_dataInit(self):
        self.funcFrames_DICT = {}
        self.funcFrames_subFrame_DICT = {}
        self.funcStart_btn_DICT = {}
        self.funcStart_lable_DICT = {}
        self.funcStart_command_DICT = {}

        self.funcStart_data_DICT_Update()

    def funcStart_data_DICT_Update(self):
        '''
        手动更新funcStart_command_DICT
        '''
        for func in self.UDS_FUNC_DICT:
            update_function = getattr(self, f'func_data_update_{func}', None)
            if update_function:
                update_function()

    # DOC 以下是func_data_update_函数，用于更新func的数据
    def func_data_update_10(self):
        pass


##############################

    def funcFrames_viewInit(self):
        # for i in range(self.stepCounts):
        for func in self.UDS_FUNC_DICT:
            self.funcFrames_DICT[func] = tk.LabelFrame(
                self.manualStepFrame, text=f"func{func}")
            self.funcFrames_DICT[func].pack()

            self.funcStart_lable_DICT[func] = tk.Label(
                self.funcFrames_DICT[func], text=self.UDS_FUNC_DICT[func], height=2, padx=2, pady=2)
            self.funcStart_lable_DICT[func].grid(
                row=1, column=1, sticky=tk.NSEW)

            def funcStart_command(func):
                def default_func(): return messagebox.showinfo(
                    'info', message=f'func{func} Start')
                return getattr(self, f'func_command_{func}', default_func)

            self.funcStart_btn_DICT[func] = tk.Button(self.funcFrames_DICT[func], text=f"func{
                func} Start", command=funcStart_command(func))

            # func} Start", command=self.funcStart_command_DICT[func])
            self.funcStart_btn_DICT[func].grid(
                row=2, column=1, padx=2, pady=2, sticky=tk.NSEW)
            self.funcFrames_subFrame_DICT[func] = tk.LabelFrame(
                self.funcFrames_DICT[func])
            self.funcFrames_subFrame_DICT[func].grid(
                row=1, rowspan=2, column=2, padx=2, pady=2, sticky=tk.NSEW)

        # 初始化之后，有了单个func的frame，可以在这里添加entry等控件
        self.funcFrames_view_update()

    def funcFrames_view_update(self):
        for func in self.UDS_FUNC_DICT:
            update_function = getattr(
                self, f'funcFrame_view_update_{func}', None)
            if update_function:
                update_function()

    def reset_funcStart_Btn(self):
        # DOC 把所有的按钮颜色恢复为默认颜色
        for _, button in self.funcStart_btn_DICT.items():
            button.configure(background='SystemButtonFace')

    def func_command_10(self):
        self.reset_funcStart_Btn()
        func = '10'
        self.funcStart_btn_DICT[func].configure(
            background='green')

        func10_input = self.func10_frame_entry_variable.get()
        option_func = int(func10_input, 16)
        self.UDS_SERVE_FUNC.serve_10(option_func=option_func,
                                     srcAddr=self.srcAddr, dstAddr=self.dstAddr)

    def funcFrame_view_update_10(self):
        func = '10'
        self.func10_label = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='10服务子功能号：')
        self.func10_label.grid(row=1, column=1, padx=10, pady=2)

        self.func10_frame_entry_variable = tk.StringVar()
        self.func10_frame_entry = tk.Entry(
            self.funcFrames_subFrame_DICT[func], textvariable=self.func10_frame_entry_variable, width=10)
        self.func10_frame_entry.grid(row=1, column=2, padx=10, pady=2)
        self.func10_frame_entry.insert(0, '01')

    def func_command_3e(self):
        # 3e服务
        def wrapper():
            self.UDS_SERVE_FUNC.serve_3e_once(
                srcAddr=self.srcAddr, dstAddr=self.dstAddr)
            while self.func3e_frame_radioButton_variable.get():
                time.sleep(3)
                self.UDS_SERVE_FUNC.serve_3e_once(
                    srcAddr=self.srcAddr, dstAddr=self.dstAddr)

            messagebox.showinfo('info', '退出3e会话保持...')
        self.sub_Thread = self.pool.submit(wrapper)
        pass

    def funcFrame_view_update_3e(self):
        func = '3e'
        self.func3e_frame_radioButton_variable = tk.BooleanVar()
        self.func3e_frame_radioButton1 = tk.Radiobutton(
            self.funcFrames_subFrame_DICT[func], text='循环关闭', variable=self.func3e_frame_radioButton_variable, value=False)
        self.func3e_frame_radioButton1.grid(
            row=1, column=1, padx=10, pady=2, sticky=tk.NSEW)

        self.func3e_frame_radioButton2 = tk.Radiobutton(
            self.funcFrames_subFrame_DICT[func], text='循环启动', variable=self.func3e_frame_radioButton_variable, value=True)
        self.func3e_frame_radioButton2.grid(
            row=2, column=1, padx=10, pady=2, sticky=tk.NSEW)

    def func_command_27(self):
        self.reset_funcStart_Btn()
        func = '27'
        self.funcStart_btn_DICT[func].configure(
            background='green')

        func27_input = self.func27_frame_entry_variable.get()
        # option_func = int(func27_input, 16)
        option_func = func27_input
        algorithm = self.unitConfig.algo
        ic_debugger(algorithm)
        self.UDS_SERVE_FUNC.serve_27_01_02(
            srcAddr=self.srcAddr, dstAddr=self.dstAddr, algo=algorithm, Learmask=option_func)

    def funcFrame_view_update_27(self):
        func = '27'

        self.func27_label = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='27服务Learmask：')
        self.func27_label.grid(row=1, column=1, padx=10, pady=2)

        # DOC 原先输入的是十进制数字
        learmask = self.unitConfig.Learmask
        # DOC 通过hex转换为16进制的字符串，这里为了方便后续的手工输入Learmask
        ic_debugger(learmask)
        # learmask = int(learmask)
        self.func27_frame_entry_variable = tk.StringVar()
        self.func27_frame_entry_variable.set(learmask)
        self.func27_frame_entry = tk.Entry(
            self.funcFrames_subFrame_DICT[func],  textvariable=self.func27_frame_entry_variable, width=10)
        self.func27_frame_entry.grid(row=1, column=2, padx=10, pady=2)

    def func_command_22(self):
        self.reset_funcStart_Btn()
        func = '22'
        self.funcStart_btn_DICT[func].configure(
            background='green')

        func22_input_1 = self.func22_frame_entry_variable1.get()
        did1 = int(func22_input_1, 16)

        func22_input_2 = self.func22_frame_entry_variable2.get()
        did2 = int(func22_input_2, 16)
        self.UDS_SERVE_FUNC.serve_22(did1=did1, did2=did2,
                                     srcAddr=self.srcAddr, dstAddr=self.dstAddr)

    def funcFrame_view_update_22(self):
        func = '22'

        # DOC  DID2
        self.func22_label1 = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='22服务DID：')
        self.func22_label1.grid(row=1, column=1, padx=10, pady=2)

        self.func22_frame_entry_variable1 = tk.StringVar()
        self.func22_frame_entry1 = tk.Entry(
            self.funcFrames_subFrame_DICT[func], textvariable=self.func22_frame_entry_variable1, width=10)
        self.func22_frame_entry1.grid(row=1, column=2, padx=10, pady=2)
        self.func22_frame_entry1.insert(0, '0xf1')

        # DOC DID2
        self.func22_label2 = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='22服务DID：')
        self.func22_label2.grid(row=2, column=1, padx=10, pady=2)

        self.func22_frame_entry_variable2 = tk.StringVar()
        self.func22_frame_entry2 = tk.Entry(
            self.funcFrames_subFrame_DICT[func], textvariable=self.func22_frame_entry_variable2, width=10)
        self.func22_frame_entry2.grid(row=2, column=2, padx=10, pady=2)
        self.func22_frame_entry2.insert(0, '0x90')

    def func_command_2e(self):
        self.reset_funcStart_Btn()
        func = '2e'
        self.funcStart_btn_DICT[func].configure(
            background='green')

        func2e_input = self.func2e_frame_entry_variable.get()
        params = [int(i, 16) for i in func2e_input.strip().split(' ')]
        self.UDS_SERVE_FUNC.sever_2e_param(
            srcAddr=self.srcAddr, dstAddr=self.dstAddr, params=params)

    def funcFrame_view_update_2e(self):
        func = '2e'
        self.func2e_label = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='2e服务参数：')
        self.func2e_label.grid(row=1, column=1, padx=10, pady=2)
        self.func2e_frame_entry_variable = tk.StringVar()
        # DOC 默认2e写入随机VIN
        params_data = [0xf1, 0x90] + [(random.randint(0x41, 0x58))
                                      for i in range(17)]
        params_str = ' '.join([f'{i:02x}' for i in params_data])
        self.func2e_frame_entry_variable.set(params_str)
        self.func2e_frame_entry = tk.Entry(
            self.funcFrames_subFrame_DICT[func], textvariable=self.func2e_frame_entry_variable, width=60)
        self.func2e_frame_entry.grid(
            row=2, column=1, padx=10, pady=2, sticky=tk.NSEW)

    def func_command_2f(self):
        self.reset_funcStart_Btn()
        func = '2f'
        self.funcStart_btn_DICT[func].configure(
            background='green')
        func2f_input = self.func2f_frame_entry_variable.get()
        params = [int(i, 16) for i in func2f_input.strip().split(' ')]
        did1 = params[0]
        did2 = params[1]
        option_func = params[2]
        data = params[3:]
        self.UDS_SERVE_FUNC.serve_2f(did1=did1, did2=did2, option_func=option_func, data=data,
                                     srcAddr=self.srcAddr, dstAddr=self.dstAddr, params=params)

    def funcFrame_view_update_2f(self):
        func = '2f'
        self.func2f_label = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='2f服务参数：')
        self.func2f_label.grid(row=1, column=1, padx=10, pady=2)
        self.func2f_frame_entry_variable = tk.StringVar()
        params_data = [0x58, 0xa0, 0x03, 1, 0, 0, 0, 1, 0, 0, 0]
        params_str = ' '.join([f'{i:02x}' for i in params_data])
        self.func2f_frame_entry_variable.set(params_str)
        self.func2f_frame_entry = tk.Entry(
            self.funcFrames_subFrame_DICT[func], textvariable=self.func2f_frame_entry_variable, width=60)
        self.func2f_frame_entry.grid(
            row=2, column=1, padx=10, pady=2, sticky=tk.NSEW)

    def func_command_28(self):
        self.reset_funcStart_Btn()
        func = '28'
        self.funcStart_btn_DICT[func].configure(
            background='green')
        func28_input = self.func28_frame_entry_variable.get()
        option_func = int(func28_input, 16)
        self.UDS_SERVE_FUNC.serve_28(option_func=option_func,
                                     srcAddr=self.srcAddr, dstAddr=self.dstAddr)

    def funcFrame_view_update_28(self):
        func = '28'
        self.func28_label = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='28服务子功能号：')
        self.func28_label.grid(row=1, column=1, padx=10, pady=2)

        self.func28_frame_entry_variable = tk.StringVar()
        self.func28_frame_entry = tk.Entry(
            self.funcFrames_subFrame_DICT[func], textvariable=self.func28_frame_entry_variable, width=10)
        self.func28_frame_entry.grid(row=1, column=2, padx=10, pady=2)
        self.func28_frame_entry.insert(0, '03')

    def func_command_31(self):
        input_31_string = self.func31_frame_entry_variable.get()
        input_31_list = input_31_string.strip().split(' ')
        sub_func = int(input_31_list[0], 16)

        # DID 是两个字节
        routineIdentifier1 = int(input_31_list[1], 16)
        routineIdentifier2 = int(input_31_list[2], 16)
        routineIdentifier = (routineIdentifier1 << 8) + routineIdentifier2

        # [ ] 待确认是否正确
        routineControlOptionRecord = int("".join(input_31_list[3:]), 16)
        self.UDS_SERVE_FUNC.serve_31(sub_func=sub_func, routineIdentifier=routineIdentifier,
                                     routineControlOptionRecord=routineControlOptionRecord, srcAddr=self.srcAddr, dstAddr=self.dstAddr)

    def funcFrame_view_update_31(self):
        func = '31'
        self.func31_label = tk.Label(
            self.funcFrames_subFrame_DICT[func], text='31服务：控制类型 例程标识1 例程标识2 用户参数')
        self.func31_label.grid(row=1, column=1, padx=10, pady=2)

        self.func31_frame_entry_variable = tk.StringVar()
        self.func31_frame_entry = tk.Entry(
            self.funcFrames_subFrame_DICT[func], textvariable=self.func31_frame_entry_variable, width=30)
        self.func31_frame_entry.grid(row=2, column=1, padx=10, pady=2)
        self.func31_frame_entry.insert(0, '01 f0 10 02')


class UI_CommonUnit_Tester(Zuds_Tester_Base):
    def __init__(self,  unitWin, masterUI, channel=0):
        super().__init__(channel)

        # 关联unitWin
        self.unitWin = unitWin
        self.masterUI = masterUI

        # 关联工作流
        srcAddr = self.unitWin.unitConfig.srcAddr
        dstAddr = self.unitWin.unitConfig.dstAddr
        self.FUNCJOB = CommonUnit_FuncJob(
            self, masterUI, dstAddr=dstAddr, srcAddr=srcAddr)


class CommonUnit_FuncJob():
    def __init__(self,  tester: UI_CommonUnit_Tester,  masterUI=None, srcAddr=0x737, dstAddr=0x7b7, *args, **kwargs):
        # 这里的tester是必选参数，如果不传递会报错
        # 如果不想传递tester，就要用父类，父类中tester给了默认值，不要用这个子类
        super().__init__(funcCounts=7, masterUI=masterUI)
        # 将主界面实例传递进来
        self.TESTER = tester
        self.serveFunc = Serve_Func(funcjob=self)

        self.srcAddr = srcAddr
        self.dstAddr = dstAddr

    def reset_funcStart_Btn(self):
        # DOC 把所有的按钮颜色恢复为默认颜色
        for _, button in self.TESTER.unitWin.funcStart_btn_DICT.items():
            button.configure(background='SystemButtonFace')

    def handleFunc10(self):
        self.reset_funcStart_Btn()
        func = '10'
        self.TESTER.unitWin.funcStart_btn_DICT[func].configure(
            background='green')

        func10_input = self.TESTER.unitWin.func10_frame_entry_variable.get()
        option_func = int(func10_input, 16)
        self.serveFunc.serve_10(option_func=option_func,
                                srcAddr=self.srcAddr, dstAddr=self.dstAddr)

    def handleFunc3e(self):
        def wrapper():
            self.serveFunc.serve_3e_once(
                srcAddr=self.srcAddr, dstAddr=self.dstAddr)
            while self.TESTER.unitWin.func3e_frame_radioButton_variable.get():
                time.sleep(3)
                self.serveFunc.serve_3e_once(
                    srcAddr=self.srcAddr, dstAddr=self.dstAddr)

            messagebox.showinfo('info', '退出3e会话保持...')
        self.sub_Thread = self.pool.submit(wrapper)

    def handleFunc27(self):
        self.reset_funcStart_Btn()
        func = '27'
        self.TESTER.unitWin.funcStart_btn_DICT[func].configure(
            background='green')

        func27_input = self.TESTER.unitWin.func27_frame_entry_variable.get()
        # option_func = int(func27_input, 16)
        option_func = func27_input
        ic_debugger(self.TESTER.unitWin.unitConfig)
        algorithm = self.TESTER.unitWin.unitConfig.algo
        ic_debugger(algorithm)
        self.serveFunc.serve_27_01_02(
            srcAddr=self.srcAddr, dstAddr=self.dstAddr, algo=algorithm, Learmask=option_func)

    def handleFunc22(self):
        self.reset_funcStart_Btn()
        func = '22'
        self.TESTER.unitWin.funcStart_btn_DICT[func].configure(
            background='green')

        func22_input_1 = self.TESTER.unitWin.func22_frame_entry_variable1.get()
        did1 = int(func22_input_1, 16)

        func22_input_2 = self.TESTER.unitWin.func22_frame_entry_variable2.get()
        did2 = int(func22_input_2, 16)
        self.serveFunc.serve_22(did1=did1, did2=did2,
                                srcAddr=self.srcAddr, dstAddr=self.dstAddr)

    def handleFunc2e(self):
        self.reset_funcStart_Btn()
        func = '2e'
        self.TESTER.unitWin.funcStart_btn_DICT[func].configure(
            background='green')

        func2e_input = self.TESTER.unitWin.func2e_frame_entry_variable.get()
        params = [int(i, 16) for i in func2e_input.strip().split(' ')]
        self.serveFunc.sever_2e_param(
            srcAddr=self.srcAddr, dstAddr=self.dstAddr, params=params)

    def handleFunc2f(self):
        self.reset_funcStart_Btn()
        func = '2f'
        self.TESTER.unitWin.funcStart_btn_DICT[func].configure(
            background='green')
        func2f_input = self.TESTER.unitWin.func2f_frame_entry_variable.get()
        params = [int(i, 16) for i in func2f_input.strip().split(' ')]
        did1 = params[0]
        did2 = params[1]
        option_func = params[2]
        data = params[3:]
        self.serveFunc.serve_2f(did1=did1, did2=did2, option_func=option_func, data=data,
                                srcAddr=self.srcAddr, dstAddr=self.dstAddr, params=params)

    def handleFunc28(self):
        self.reset_funcStart_Btn()
        func = '28'
        self.TESTER.unitWin.funcStart_btn_DICT[func].configure(
            background='green')
        func28_input = self.TESTER.unitWin.func28_frame_entry_variable.get()
        option_func = int(func28_input, 16)
        self.serveFunc.serve_28(option_func=option_func,
                                srcAddr=self.srcAddr, dstAddr=self.dstAddr)

    def handleFunc31(self):
        pass
        self.serveFunc.serve_31(sub_func=0x01, routineIdentifier=0xf010,
                                routineControlOptionRecord=0x02, srcAddr=0x716, dstAddr=0x796)


def main():
    pass


if __name__ == '__main__':
    a = 5+5
    main()
